import window from '@ohos.window';
import emitter from '@ohos.events.emitter';
import HashMap from '@ohos.util.HashMap';
import util from '@ohos.util';
import Hash from '@ohos.file.hash';
import { Callback } from '@ohos.base';
import fs, { ReadOptions } from '@ohos.file.fs';
import buffer from '@ohos.buffer';
import display from '@ohos.display';

let eventIdMap: HashMap<string, number> = new HashMap();
let lastEventId: number = 13090; //随便写的大数，防止和其它地方重复

let offIdMap: HashMap<number, Function> = new HashMap();
let lastOffId: number = 1;

let offIdArrayMap: HashMap<string, Array<number>> = new HashMap();

let densityPixels: number | undefined

export class WTR {
  private static getEventId(key: string) {
    if (!eventIdMap.hasKey(key)) {
      eventIdMap.set(key, lastEventId++)
    }
    return eventIdMap.get(key)
  }

  private static getOffIdArray(key: string) {
    if (!offIdArrayMap.hasKey(key)) {
      offIdArrayMap.set(key, [])
    }
    return offIdArrayMap.get(key)
  }

  //应用内通知
  //返回值用于取消单个订阅
  static on(key: string, callback: (data: { [key: string]: any; }) => void): number {

    let func = (eventData: emitter.EventData) => {
      callback(eventData.data)
    }

    let offId = lastOffId;
    offIdMap.set(lastOffId++, func)

    emitter.on({ eventId: this.getEventId(key) }, func)

    this.getOffIdArray(key).push(offId)

    return offId;
  }

  static off(key: string, offId?: number) {
    if (offId) {
      //移除单个
      if (offIdMap.hasKey(offId)) {
        emitter.off(this.getEventId(key), offIdMap.get(offId) as Callback<emitter.EventData>);
        offIdMap.remove(offId)
        let arr = this.getOffIdArray(key);
        for (let i = 0; i < arr.length; i++) {
          if (arr[i] == offId) {
            arr.splice(i, 1);
            break
          }
        }
      }
    } else {
      //移除所有
      emitter.off(this.getEventId(key))
      this.getOffIdArray(key).forEach((one) => {
        if (offIdMap.hasKey(one)) {
          offIdMap.remove(one)
        }
      })
      offIdArrayMap.set(key, [])
    }
  }

  static emit(key: string, data?: { [key: string]: any; }) {
    if (data) {
      emitter.emit({ eventId: this.getEventId(key) }, { data: data })
    } else {
      emitter.emit({ eventId: this.getEventId(key) })
    }
  }

  //物理像素与逻辑像素比例
  static densityPixels() {
    if (densityPixels) {
      return densityPixels
    }
    densityPixels = display.getDefaultDisplaySync().densityPixels
    return densityPixels
  }

  /*
    mainWindow
    需要先设置 WTR.mainWindow
    onWindowStageCreate(windowStage: window.WindowStage) {
      windowStage.loadContent('pages/Index', (err, data) => {
          WTR.mainWindow = windowStage.getMainWindowSync();
      });
    }
   */
  static mainWindow: window.Window | undefined

  //获取逻辑宽度
  static windowWidth(): number {
    if (this.mainWindow) {
      return this.mainWindow.getWindowProperties().windowRect.width / this.densityPixels()
    }
    return 0
  }

  //获取逻辑高度
  static windowHeight(): number {
    if (this.mainWindow) {
      return this.mainWindow.getWindowProperties().windowRect.height / this.densityPixels()
    }
    return 0
  }

  static safeTop() {
    if (this.mainWindow) {
      let rect = this.mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM).topRect;
      return (rect.top + rect.height) / this.densityPixels()
    }
    return 0
  }

  static safeBottom() {
    if (this.mainWindow) {
      let rect = this.mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR).bottomRect;
      return Math.ceil((rect.height / this.densityPixels()) / 2.0);
    }
    return 0
  }

  static safeLeft() {
    if (this.mainWindow) {
      let rect = this.mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM).leftRect;
      return (rect.left + rect.width) / this.densityPixels();
    }
    return 0
  }

  static safeRight() {
    if (this.mainWindow) {
      let rect = this.mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM).rightRect;
      return rect.width / this.densityPixels();
    }
    return 0
  }

  static randomColor() {
    return `rgb(${(255 * Math.random()).toFixed(0)},${(255 * Math.random()).toFixed(0)},${(255 * Math.random()).toFixed(0)})`
  }


  static stringToUint8Array(str: string): Uint8Array {
    let textEncoder = new util.TextEncoder();
    return textEncoder.encodeInto(str);
  }

  static uint8ArrayToString(arr: Uint8Array): string {
    let textDecoder = util.TextDecoder.create("utf-8", { ignoreBOM: true });
    let str = textDecoder.decodeWithStream(arr, { stream: false })
    return str;
  }

  static stringByRemoveLastPathComponent(str: string): string {
    let index = str.lastIndexOf('/');
    if (index < 0) {
      return str
    }
    return str.substring(0, index)
  }

  static stringByRemovePathExtension(str: string): string {
    let index = str.lastIndexOf('.');
    let indexG = str.lastIndexOf('/');
    if (index < 0 || index < indexG) {
      return str
    }
    return str.substring(0, index)
  }

  static pathExtension(str: string) {
    let index = str.lastIndexOf('.');
    let indexG = str.lastIndexOf('/');
    if (index < 0 || index < indexG) {
      return ''
    }
    return str.substring(index + 1)
  }

  //读取文件为字符串
  static readFileToString(path: string): string {
    if (!fs.accessSync(path)) {
      return ''
    }
    let bufferSize = fs.lstatSync(path).size;
    if (bufferSize <= 0) {
      return ''
    }
    let file = fs.openSync(path, fs.OpenMode.READ_ONLY);
    let arrayBuffer = new ArrayBuffer(bufferSize);
    let readOptions: ReadOptions = {
      offset: 0,
      length: arrayBuffer.byteLength
    };
    let readLen = fs.readSync(file.fd, arrayBuffer, readOptions);
    fs.closeSync(file);

    return buffer.from(arrayBuffer, 0, readLen).toString()
  }

  //保存字符串到文件
  static saveStringToFile(str: string, path: string) {
    let file = fs.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
    fs.writeSync(file.fd, str)
    fs.closeSync(file);
  }

  static fileMd5(path: string, callback: (md5Str: string) => void): void {
    Hash.hash(path, "md5", (err: Error, str: string) => {
      if (err) { //BusinessError
        // console.error("md5计算失败: " + err.message);
        callback("");
      } else {
        callback(str);
      }
    });
  }

  static base64Encode(src: Uint8Array): string {
    let base64 = new util.Base64Helper();
    return base64.encodeToStringSync(src);
  }

  static base64Decode(src: string): Uint8Array {
    let base64 = new util.Base64Helper();
    return base64.decodeSync(src);
  }

  /*
   * JSON解析成对应类型
   * 例子：let user: UserModel = WTR.JSONParse(jsonStr,UserModel)
   * 多层嵌套情况使用下面的属性装饰器 @JSONType(Class)
   * */
  static JSONParse(jsonStr: string | object, toClass: any): any {
    if (typeof toClass !== 'function') {
      console.error("第二个参数需要传入class")
      return undefined
    }
    let jsonObj = typeof jsonStr === 'string' ? JSON.parse(jsonStr) : jsonStr
    if (Array.isArray(jsonObj)) {
      let arr = jsonObj;
      let list = new Array()
      for (let i = 0; i < arr.length; i++) {
        list.push(this.JSONParse(arr[i], toClass))
      }
      return list;
    }
    if (jsonObj) {
      let toObj = new toClass();
      for (let cname in jsonObj) {
        let wtr_t = toObj["wtr_type_"+cname];
        let cJsonObj = jsonObj[cname];
        if (wtr_t !== undefined) {
          toObj[cname] = this.JSONParse(cJsonObj, wtr_t)
        } else {
          toObj[cname] = cJsonObj;
        }
      }
      return toObj;
    }
    return undefined;
  }
}

/*
 * 属性装饰器 在属性上面写 @JSONType(class名) 用于JSONParse解析的时候读取类型,支持数组
 * 例子：
 * class A {
 *  @JSONType(B)
 *  b?: B
 *  @JSONType(C)
 *  c?:C[]
 * }
 * */
export function JSONType(t: any): PropertyDecorator {
  return (target: Object, propertyKey: string | symbol) => {
    target["wtr_type_"+propertyKey.toString()] = t
  }
}